package pl.ydp.ivy;

import java.io.IOException;
import java.text.ParseException;

import org.apache.ivy.Ivy;
import org.apache.ivy.core.event.IvyEvent;
import org.apache.ivy.core.event.IvyListener;
import org.apache.ivy.core.event.download.EndArtifactDownloadEvent;
import org.apache.ivy.core.event.download.PrepareDownloadEvent;
import org.apache.ivy.core.event.download.StartArtifactDownloadEvent;
import org.apache.ivy.core.event.resolve.EndResolveDependencyEvent;
import org.apache.ivy.core.event.resolve.StartResolveDependencyEvent;
import org.apache.ivy.core.event.retrieve.EndRetrieveArtifactEvent;
import org.apache.ivy.core.module.descriptor.Artifact;
import org.apache.ivy.core.report.ResolveReport;
import org.apache.ivy.core.retrieve.RetrieveOptions;
import org.apache.ivy.plugins.repository.TransferEvent;
import org.apache.ivy.plugins.repository.TransferListener;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.SubProgressMonitor;

public class Retriever {
    private static final int DOWNLOAD_MONITOR_LENGTH = 100;
    private static final int KILO_BITS_UNIT = 1024;
    private static final int MONITOR_LENGTH = 1000;
    
	private IIvyProvider ivyProvider;
	IProgressMonitor monitor;
	
	private int currentLength;
	private long expectedTotalLength;
	private SubProgressMonitor dlmonitor;
	private int workPerArtifact;
	private ProgressMonitor progressListener;
	private RetrieveMonitor retrieveMonitor;
	

	public Retriever(IIvyProvider ivyProvider, IProgressMonitor monitor) {
		this.ivyProvider = ivyProvider;
		this.monitor = monitor;
	}
	
	public void setRetrieveMonitor( IRetrieveMonitor monitor ) {
		if ( retrieveMonitor==null && monitor!=null ) {
			retrieveMonitor = new RetrieveMonitor();
			retrieveMonitor.monitor = monitor;
		}
	}
	
	public void retreieve() throws ParseException, IOException {
		Ivy ivy = createIvy();
		if ( monitor!=null ) {
			progressListener = new ProgressMonitor();
			
			ivy.getEventManager().addIvyListener( progressListener);
			ivy.getEventManager().addTransferListener( progressListener );
		}
		if ( retrieveMonitor!=null )
			ivy.getEventManager().addIvyListener(retrieveMonitor);
		
		RetrieveOptions retrOptions = new RetrieveOptions();
		retrOptions.setSync( true );
		
		ResolveReport report = ivy.resolve(
				ivyProvider.getIvyFile() );
		ivy.retrieve(report.getModuleDescriptor().getModuleRevisionId(), ivyProvider.getArtifactPath(), retrOptions );
	}
	
	protected Ivy createIvy() throws ParseException, IOException {
		return ivyProvider.getIvy();
	}
	
	protected class RetrieveMonitor implements IvyListener {
		IRetrieveMonitor monitor;
		
	    public void progress(IvyEvent event) {
	    	if ( event instanceof EndRetrieveArtifactEvent ) {
	    		EndRetrieveArtifactEvent erae = (EndRetrieveArtifactEvent)event;
	    		
	    		monitor.retrieved(
		    		erae.getReport().getArtifact(), 
		    		erae.getDestFile() );
	    	}
	    }
	}

	protected class ProgressMonitor implements TransferListener, IvyListener {
	    public void transferProgress(TransferEvent evt) {
	        switch (evt.getEventType()) {
	            case TransferEvent.TRANSFER_INITIATED:
	                monitor.setTaskName("downloading " + evt.getResource());
	                break;
	            case TransferEvent.TRANSFER_STARTED:
	                currentLength = 0;
	                if (evt.isTotalLengthSet()) {
	                    expectedTotalLength = evt.getTotalLength();
	                    dlmonitor
	                            .beginTask("downloading " + evt.getResource(), DOWNLOAD_MONITOR_LENGTH);
	                }
	                break;
	            case TransferEvent.TRANSFER_PROGRESS:
	                if (expectedTotalLength > 1) {
	                    currentLength += evt.getLength();
	                    int progress = (int) (currentLength * DOWNLOAD_MONITOR_LENGTH / expectedTotalLength);
	                    dlmonitor.worked(progress);
	                    monitor.subTask((currentLength / KILO_BITS_UNIT) + " / "
	                            + (expectedTotalLength / KILO_BITS_UNIT) + "kB");
	                }
	                break;
	            default:
	        }
	    }
	
	    public void progress(IvyEvent event) {
	        if (event instanceof TransferEvent) {
	            if (dlmonitor != null) {
	                transferProgress((TransferEvent) event);
	            }
	        } else if (event instanceof PrepareDownloadEvent) {
	            PrepareDownloadEvent pde = (PrepareDownloadEvent) event;
	            Artifact[] artifacts = pde.getArtifacts();
	            if (artifacts.length > 0) {
	                workPerArtifact = MONITOR_LENGTH / artifacts.length;
	            }
	        } else if (event instanceof StartArtifactDownloadEvent) {
	            StartArtifactDownloadEvent evt = (StartArtifactDownloadEvent) event;
	            monitor.setTaskName("downloading " + evt.getArtifact());
	            if (dlmonitor != null) {
	                dlmonitor.done();
	            }
	            dlmonitor = new SubProgressMonitor(monitor, workPerArtifact);
	        } else if (event instanceof EndArtifactDownloadEvent) {
	            if (dlmonitor != null) {
	                dlmonitor.done();
	            }
	            monitor.subTask(" ");
	            dlmonitor = null;
	        } else if (event instanceof StartResolveDependencyEvent) {
	            StartResolveDependencyEvent ev = (StartResolveDependencyEvent) event;
	            monitor.subTask("resolving " + ev.getDependencyDescriptor().getDependencyRevisionId());
	        } else if (event instanceof EndResolveDependencyEvent) {
	            monitor.subTask(" ");
	        }
	    }
	}
}
